package cn.tedu.csmall.passport.service.impl;


import cn.tedu.csmall.commons.ex.ServiceException;
import cn.tedu.csmall.commons.pojo.po.AdminLoginInfoPo;
import cn.tedu.csmall.commons.pojo.vo.PageData;
import cn.tedu.csmall.commons.util.PageInfoToPageDataConverter;
import cn.tedu.csmall.commons.web.ServiceCode;
import cn.tedu.csmall.passport.mapper.AdminMapper;
import cn.tedu.csmall.passport.mapper.AdminRoleMapper;
import cn.tedu.csmall.passport.pojo.entity.Admin;
import cn.tedu.csmall.passport.pojo.entity.AdminRole;
import cn.tedu.csmall.passport.pojo.param.AdminAddNewParam;
import cn.tedu.csmall.passport.pojo.param.AdminLoginInfoParam;
import cn.tedu.csmall.passport.pojo.param.AdminUpdateInfoParam;
import cn.tedu.csmall.passport.pojo.vo.AdminListItemVo;
import cn.tedu.csmall.passport.pojo.vo.AdminStandardVo;

import cn.tedu.csmall.passport.repository.AdminJwtRepository;
import cn.tedu.csmall.passport.security.AdminDetails;
import cn.tedu.csmall.passport.service.AdminService;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class AdminServiceImpl implements AdminService {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;
    @Value("${csmall.jwt.duration-in-minute}")
    private Long durationInMinute;
    @Autowired
    AdminMapper adminMapper;
    @Autowired
    AdminRoleMapper adminRoleMapper;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    AdminJwtRepository adminJwtRepository;

    public AdminServiceImpl() {
        log.debug("创建业务类对象：AdminServiceImpl");
    }


    @Override
    public String login(AdminLoginInfoParam adminLoginInfoParam,String remoteAdrr,String userAgent) {
        log.debug("开始处理【管理员登录】的业务，参数：{}", adminLoginInfoParam);
        // 创建认证时所需的参数对象
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                adminLoginInfoParam.getUsername(),
                adminLoginInfoParam.getPassword());
        log.debug("------------------");
        // 执行认证，并获取认证结果
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("验证登录完成，认证结果：{}", authenticateResult);

        // 从认证结果中取出所需的数据
        Object principal = authenticateResult.getPrincipal();
        AdminDetails adminDetails = (AdminDetails) principal;
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();

        // 生成JWT
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", adminDetails.getId());
        claims.put("username", adminDetails.getUsername());
//        claims.put("authoritiesJsonString", JSON.toJSONString(authorities));
        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + durationInMinute * 1000 * 60))
                // Verify Signature
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // 生成
                .compact();
        log.debug("生成了此管理员的信息对应的JWT：{}", jwt);

        //需要缓存到Redis中的数据
        AdminLoginInfoPo adminLoginInfoPo = new AdminLoginInfoPo();
        adminLoginInfoPo.setId(adminDetails.getId());
        adminLoginInfoPo.setEnable(adminDetails.isEnabled() ? 1 : 0);
        adminLoginInfoPo.setRemoteAddr(remoteAdrr);
        adminLoginInfoPo.setUserAgent(userAgent);
        adminLoginInfoPo.setAuthorityListJsonString(JSON.toJSONString(authorities));
        adminJwtRepository.saveLoginInfo(jwt,adminLoginInfoPo,durationInMinute * 60 * 1000);
        log.debug("向Redis中写入此管理员的登录信息:{}",adminLoginInfoPo);


        return jwt;

        // ===== 使用JWT后不再需要以下2行代码 =====
        // 将认证结果存入到SecurityContext中
        // SecurityContext securityContext = SecurityContextHolder.getContext();
        // securityContext.setAuthentication(authenticateResult);
    }


    @Override
    public void addNew(AdminAddNewParam adminAddNewParam) {
        log.debug("开始处理【新增管理员】的业务，参数:{}",adminAddNewParam);

        //检查管理员用户名是否已被占用，若已被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",adminAddNewParam.getUsername());
        int countByUsername = adminMapper.selectCount(queryWrapper);
        log.debug("根据用户名统计匹配的管理员数量:{}"+countByUsername);
        if (countByUsername > 0){
            String message = "添加管理员失败，用户名已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }

        //检查管理员手机号码是否被占用，若果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("phone",adminAddNewParam.getPhone());
        int countByPhone = adminMapper.selectCount(queryWrapper2);
        log.debug("根据手机号统计匹配的管理员数量:{}"+countByPhone);
        if (countByPhone > 0){
            String message = "添加管理员失败，手机号已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }
        //检查管理员电子邮箱是否被占用，若果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("email",adminAddNewParam.getEmail());
        int countByEmail = adminMapper.selectCount(queryWrapper3);
        log.debug("根据手机号统计匹配的管理员数量:{}"+countByEmail);
        if (countByEmail > 0){
            String message = "添加管理员失败，邮箱已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }

        //将管理员数据写入到数据库中
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewParam,admin);
        admin.setLastLoginIp(null);
        admin.setLoginCount(0);
        admin.setGmtLastLogin(null);
        admin.setGmtCreate(LocalDateTime.now());
        admin.setGmtModified(LocalDateTime.now());
        String passwordEncode = passwordEncoder.encode(admin.getPassword());
        admin.setPassword(passwordEncode);
        int rows = adminMapper.insert(admin);
        if (rows != 1){
            String message = "添加管理员失败，服务器忙，请稍后再试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }
        log.debug("将新的管理员数据插入到数据库，完成!");

        // 将管理员与角色的关联数据写入到数据库中
        Long[] roleIds = adminAddNewParam.getRoleIds();
        AdminRole[] adminRoles = new AdminRole[roleIds.length];
        LocalDateTime now = LocalDateTime.now();
        for (int i = 0; i < adminRoles.length; i++) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(roleIds[i]);
            adminRole.setGmtCreate(now);
            adminRole.setGmtModified(now);
            adminRoles[i] = adminRole;
        }
        rows = adminRoleMapper.insertBatch(adminRoles);
        if (rows != adminRoles.length){
            String message = "添加管理员失败，服务器忙，请稍后再试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }
        log.debug("将新的管理员与角色的关联数据插入到数据库，完成!");
    }

    @Override
    public PageData<AdminListItemVo> list(Integer pageNum) {
        Integer pageSize = 5;
        return list(pageNum,pageSize);
    }

    @Override
    public PageData<AdminListItemVo> list(Integer pageNum, Integer pageSize) {
        log.debug("开始处理【查询管理员列表】的业务，页码:{},每页条数:{}",pageNum,pageSize);
        PageHelper.startPage(pageNum,pageSize);
        List<AdminListItemVo> list = adminMapper.list();
        PageInfo<AdminListItemVo> pageInfo = new PageInfo<>(list);
        PageData<AdminListItemVo> pageData = PageInfoToPageDataConverter.convert(pageInfo);
        log.debug("返回分页数据:{}",pageData);
        return pageData;
    }

    @Override
    public void delete(Long id) {
        log.debug("开始处理【根据ID删除管理员】的业务，参数:{}",id);
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",id);
        int countById = adminMapper.selectCount(queryWrapper);
        log.debug("根据管理员id匹配对应的管理员数量:{}",countById);
        if (countById == 0){
            String message = "删除管理员失败，没有此数据";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }

        //根据id删除管理员
        int rows = adminMapper.deleteById(id);
        if (rows != 1){
            String message = "删除管理员失败,服务器繁忙，请稍后再试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_DELETE,message);
        }

        //检查根据管理员id查询在管理员与角色关联表中有无数据
        QueryWrapper<AdminRole> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("admin_id",id);
        int countByAdminId = adminRoleMapper.selectCount(queryWrapper2);
        log.debug("根据管理员id匹配对应的管理员角色数量:{}",countByAdminId);
        //根据管理员id删除对应的管理员与角色的关联
        if (countById != 0){
            adminRoleMapper.deleteByAdminId(id);
        }
        log.debug("删除管理员成功!");
    }

    @Override
    public void updatePasswordById(Long id, String password) {
        log.debug("开始处理【根据ID修改管理员密码】的业务，id:{},新password:{}",id,password);
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",id);
        int countById = adminMapper.selectCount(queryWrapper);
        log.debug("根据管理员id匹配对应的管理员数量:"+countById);
        if (countById == 0){
            String message = "修改管理员密码失败，没有此管理员!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }

        //执行修改密码操作
        String passwordEncode = passwordEncoder.encode(password);
        int rows = adminMapper.updatePasswordById(id, passwordEncode);
        if (rows != 1){
            String message = "修改管理员密码失败,服务器繁忙，请稍后再试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE,message);
        }
        log.debug("修改管理员密码成功!");
    }

    @Override
    public void updateInfoById(Long id, AdminUpdateInfoParam adminUpdateInfoParam) {
        log.debug("开始处理【根据ID修改管理员基本信息】的业务，id:{},新数据:{}",id,adminUpdateInfoParam);
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",id);
        int countById = adminMapper.selectCount(queryWrapper);
        if (countById == 0){
            String message = "修改管理员基本信息失败，没有此管理员!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }

        //检查管理员用户名是否被占用，若被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("username",adminUpdateInfoParam.getUsername())
                .ne("id",id);
        int countByUsername = adminMapper.selectCount(queryWrapper1);
        log.debug("根据管理员用户名匹配其他用户名的数量:{}",countByUsername);
        if (countByUsername > 0){
            String message = "修改管理员基本信息失败，用户名已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }

        //检查管理员手机号是否被占用，若被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("phone",adminUpdateInfoParam.getPhone())
                .ne("id",id);
        int countByPhone = adminMapper.selectCount(queryWrapper2);
        log.debug("根据管理员手机号匹配其他用户名的数量:{}",countByPhone);
        if (countByPhone > 0){
            String message = "修改管理员基本信息失败，手机号已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }

        //检查管理员手机号是否被占用，若被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("email",adminUpdateInfoParam.getEmail())
                .ne("id",id);
        int countByEmail = adminMapper.selectCount(queryWrapper3);
        log.debug("根据管理员邮箱匹配其他用户名的数量:{}",countByEmail);
        if (countByEmail > 0){
            String message = "修改管理员基本信息失败，邮箱已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }

        //修改管理员基本信息
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminUpdateInfoParam,admin);
        admin.setId(id);
        admin.setGmtModified(LocalDateTime.now());
        int rows = adminMapper.updateById(admin);
        if (rows != 1){
            String message = "修改管理员信息失败，服务器繁忙，请稍后再试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE,message);
        }
        log.debug("修改管理员信息成功!");

    }

    @Override
    public AdminStandardVo getStandardById(Long id) {
        log.debug("开始处理【根据ID查询管理员详情】的业务,参数:{}",id);
        AdminStandardVo adminStandardVo = adminMapper.getStandardById(id);
        if (adminStandardVo == null){
            String message = "查询管理员详情失败，没有此管理员";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }
        return adminStandardVo;
    }
}
